// system_error standard header
#pragma once
#ifndef _SYSTEM_ERROR_
#define _SYSTEM_ERROR_
#ifndef RC_INVOKED
#include <cerrno>
#include <cstdlib> // for strerror
#include <stdexcept> // for runtime_error
#include <xcall_once.h>
#include <xerrc.h>

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new

_STD_BEGIN
// ENUM CLASS io_errc
enum class io_errc { // error codes for ios_base::failure
    stream = 1
};

// STRUCT TEMPLATE is_error_code_enum
template <class _Enum>
struct is_error_code_enum : false_type { // tests for error_code enumeration
};

template <>
struct is_error_code_enum<io_errc> : true_type { // tests for error_condition enumeration
};

template <class _Ty>
_INLINE_VAR constexpr bool is_error_code_enum_v = is_error_code_enum<_Ty>::value;

// STRUCT TEMPLATE is_error_condition_enum
template <class _Enum>
struct is_error_condition_enum : false_type { // tests for error_condition enumeration
};

template <>
struct is_error_condition_enum<errc> : true_type { // tests for error_condition enumeration
};

template <class _Ty>
_INLINE_VAR constexpr bool is_error_condition_enum_v = is_error_condition_enum<_Ty>::value;

class error_code;
class error_condition;
_NODISCARD error_code make_error_code(errc) noexcept;
_NODISCARD error_code make_error_code(io_errc) noexcept;
_NODISCARD error_condition make_error_condition(errc) noexcept;
_NODISCARD error_condition make_error_condition(io_errc) noexcept;

// CLASS error_category
class error_category;

_NODISCARD const error_category& generic_category() noexcept;
_NODISCARD const error_category& iostream_category() noexcept;
_NODISCARD const error_category& system_category() noexcept;

class __declspec(novtable) error_category { // categorize an error
public:
    /* constexpr */ error_category() noexcept { // TRANSITION
        // default constructor
        _Addr = reinterpret_cast<uintptr_t>(this);
    }

    virtual ~error_category() noexcept {}

    _NODISCARD virtual const char* name() const noexcept = 0;

    _NODISCARD virtual string message(int _Errval) const = 0;

    _NODISCARD virtual error_condition default_error_condition(int _Errval) const noexcept;

    _NODISCARD virtual bool equivalent(int _Errval, const error_condition& _Cond) const noexcept;

    _NODISCARD virtual bool equivalent(const error_code& _Code, int _Errval) const noexcept;

    _NODISCARD bool operator==(const error_category& _Right) const noexcept { // compare categories for equality
        return _Addr == _Right._Addr;
    }

    _NODISCARD bool operator!=(const error_category& _Right) const noexcept { // compare categories for inequality
        return !(*this == _Right);
    }

    _NODISCARD bool operator<(const error_category& _Right) const noexcept { // compare categories for order
        return _Addr < _Right._Addr;
    }

    error_category(const error_category&) = delete;
    error_category& operator=(const error_category&) = delete;

protected:
    uintptr_t _Addr;

    enum : uintptr_t { // imaginary addresses for Standard error_category objects
        _Future_addr   = 1,
        _Generic_addr  = 3,
        _Iostream_addr = 5,
        _System_addr   = 7
    };
};

// CLASS error_code
class error_code { // store an implementation-specific error code and category
public:
    error_code() noexcept : _Myval(0), _Mycat(&system_category()) { // construct non-error
    }

    error_code(int _Val, const error_category& _Cat) noexcept
        : _Myval(_Val), _Mycat(&_Cat) { // construct from error code and category
    }

    template <class _Enum, enable_if_t<is_error_code_enum_v<_Enum>, int> = 0>
    error_code(_Enum _Errcode) noexcept : _Myval(0), _Mycat(nullptr) { // construct from enumerated error code
        *this = make_error_code(_Errcode); // using ADL
    }

    void assign(int _Val, const error_category& _Cat) noexcept { // assign error code and category
        _Myval = _Val;
        _Mycat = &_Cat;
    }

    template <class _Enum, enable_if_t<is_error_code_enum_v<_Enum>, int> = 0>
    error_code& operator=(_Enum _Errcode) noexcept { // assign enumerated error code

        *this = make_error_code(_Errcode); // using ADL
        return *this;
    }

    void clear() noexcept { // assign non-error
        _Myval = 0;
        _Mycat = &system_category();
    }

    _NODISCARD int value() const noexcept { // get error code
        return _Myval;
    }

    _NODISCARD const error_category& category() const noexcept { // get category
        return *_Mycat;
    }

    _NODISCARD error_condition default_error_condition() const noexcept;

    _NODISCARD string message() const { // get name of error code
        return category().message(value());
    }

    explicit operator bool() const noexcept { // test for actual error
        return value() != 0;
    }

private:
    int _Myval; // the stored error number
    const error_category* _Mycat; // pointer to error category
};

// CLASS error_condition
class error_condition { // store an abstract error code and category
public:
    error_condition() noexcept : _Myval(0), _Mycat(&generic_category()) { // construct non-error
    }

    error_condition(int _Val, const error_category& _Cat) noexcept
        : _Myval(_Val), _Mycat(&_Cat) { // construct from error code and category
    }

    template <class _Enum, enable_if_t<is_error_condition_enum_v<_Enum>, int> = 0>
    error_condition(_Enum _Errcode) noexcept : _Myval(0), _Mycat(nullptr) { // construct from enumerated error code
        *this = make_error_condition(_Errcode); // using ADL
    }

    void assign(int _Val, const error_category& _Cat) noexcept { // assign error code and category
        _Myval = _Val;
        _Mycat = &_Cat;
    }

    template <class _Enum, enable_if_t<is_error_condition_enum_v<_Enum>, int> = 0>
    error_condition& operator=(_Enum _Errcode) noexcept { // assign enumerated error code

        *this = make_error_condition(_Errcode); // using ADL
        return *this;
    }

    void clear() noexcept { // assign non-error
        _Myval = 0;
        _Mycat = &generic_category();
    }

    _NODISCARD int value() const noexcept { // get error code
        return _Myval;
    }

    _NODISCARD const error_category& category() const noexcept { // get category
        return *_Mycat;
    }

    _NODISCARD string message() const { // get name of error code
        return category().message(value());
    }

    explicit operator bool() const noexcept { // test for actual error
        return value() != 0;
    }

private:
    int _Myval; // the stored error number
    const error_category* _Mycat; // pointer to error category
};

// operator== FOR error_code/error_condition
_NODISCARD inline bool operator==(
    const error_code& _Left, const error_code& _Right) noexcept { // test errors for equality
    return _Left.category() == _Right.category() && _Left.value() == _Right.value();
}

_NODISCARD inline bool operator==(
    const error_code& _Left, const error_condition& _Right) noexcept { // test errors for equality
    return _Left.category().equivalent(_Left.value(), _Right) || _Right.category().equivalent(_Left, _Right.value());
}

_NODISCARD inline bool operator==(
    const error_condition& _Left, const error_code& _Right) noexcept { // test errors for equality
    return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value());
}

_NODISCARD inline bool operator==(
    const error_condition& _Left, const error_condition& _Right) noexcept { // test errors for equality
    return _Left.category() == _Right.category() && _Left.value() == _Right.value();
}

// operator!= FOR error_code/error_condition
_NODISCARD inline bool operator!=(
    const error_code& _Left, const error_code& _Right) noexcept { // test errors for inequality
    return !(_Left == _Right);
}

_NODISCARD inline bool operator!=(
    const error_code& _Left, const error_condition& _Right) noexcept { // test errors for inequality
    return !(_Left == _Right);
}

_NODISCARD inline bool operator!=(
    const error_condition& _Left, const error_code& _Right) noexcept { // test errors for inequality
    return !(_Left == _Right);
}

_NODISCARD inline bool operator!=(
    const error_condition& _Left, const error_condition& _Right) noexcept { // test errors for inequality
    return !(_Left == _Right);
}

// operator< FOR error_code/error_condition
_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { // test if _Left < _Right
    return _Left.category() < _Right.category()
           || (_Left.category() == _Right.category() && _Left.value() < _Right.value());
}

_NODISCARD inline bool operator<(
    const error_condition& _Left, const error_condition& _Right) noexcept { // test if _Left < _Right
    return _Left.category() < _Right.category()
           || (_Left.category() == _Right.category() && _Left.value() < _Right.value());
}

// VIRTUALS FOR error_category
_NODISCARD inline error_condition error_category::default_error_condition(int _Errval) const
    noexcept { // make error_condition for error code
    return error_condition(_Errval, *this);
}

_NODISCARD inline bool error_category::equivalent(int _Errval, const error_condition& _Cond) const
    noexcept { // test if error code same condition
    return default_error_condition(_Errval) == _Cond;
}

_NODISCARD inline bool error_category::equivalent(const error_code& _Code, int _Errval) const
    noexcept { // test if conditions same for this category
    return *this == _Code.category() && _Code.value() == _Errval;
}

// MEMBER FUNCTIONS for error_code
_NODISCARD inline error_condition error_code::default_error_condition() const
    noexcept { // make error_condition for error code
    return category().default_error_condition(value());
}

// FUNCTION make_error_code
_NODISCARD inline error_code make_error_code(errc _Errno) noexcept { // make an error_code
    return error_code((int) _Errno, generic_category());
}

_NODISCARD inline error_code make_error_code(io_errc _Errno) noexcept { // make an error_code
    return error_code((int) _Errno, iostream_category());
}

// FUNCTION make_error_condition
_NODISCARD inline error_condition make_error_condition(errc _Errno) noexcept { // make an error_condition
    return error_condition((int) _Errno, generic_category());
}

_NODISCARD inline error_condition make_error_condition(io_errc _Errno) noexcept { // make an error_condition
    return error_condition((int) _Errno, iostream_category());
}

// STRUCT TEMPLATE SPECIALIZATION hash
template <>
struct hash<error_code> { // hash functor for error_code
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef error_code argument_type;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t result_type;

    _NODISCARD size_t operator()(const error_code& _Keyval) const
        noexcept { // hash _Keyval to size_t value by pseudorandomizing transform
        return hash<int>{}(_Keyval.value());
    }
};

template <>
struct hash<error_condition> { // hash functor for error_condition
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef error_condition argument_type;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t result_type;

    _NODISCARD size_t operator()(const error_condition& _Keyval) const
        noexcept { // hash _Keyval to size_t value by pseudorandomizing transform
        return hash<int>{}(_Keyval.value());
    }
};

// CLASS system_error
class _System_error : public runtime_error { // base of all system-error exceptions
private:
    static string _Makestr(error_code _Errcode, string _Message) { // compose error message
        if (!_Message.empty()) {
            _Message.append(": ");
        }

        _Message.append(_Errcode.message());
        return _Message;
    }

protected:
    _System_error(error_code _Errcode, const string& _Message)
        : runtime_error(_Makestr(_Errcode, _Message)),
          _Mycode(_Errcode) { // construct from error code and message string
    }

    error_code _Mycode; // the stored error code
};

class system_error : public _System_error { // base of all system-error exceptions
private:
    using _Mybase = _System_error;

public:
    system_error(error_code _Errcode) : _Mybase(_Errcode, "") { // construct from error code
    }

    system_error(error_code _Errcode, const string& _Message)
        : _Mybase(_Errcode, _Message) { // construct from error code and message string
    }

    system_error(error_code _Errcode, const char* _Message)
        : _Mybase(_Errcode, _Message) { // construct from error code and message string
    }

    system_error(int _Errval, const error_category& _Errcat)
        : _Mybase(error_code(_Errval, _Errcat), "") { // construct from error code components
    }

    system_error(int _Errval, const error_category& _Errcat, const string& _Message)
        : _Mybase(error_code(_Errval, _Errcat), _Message) { // construct from error code components and message string
    }

    system_error(int _Errval, const error_category& _Errcat, const char* _Message)
        : _Mybase(error_code(_Errval, _Errcat), _Message) { // construct from error code components and message string
    }

    _NODISCARD const error_code& code() const noexcept { // return stored error code
        return _Mycode;
    }

#if _HAS_EXCEPTIONS

#else // _HAS_EXCEPTIONS
protected:
    virtual void _Doraise() const { // perform class-specific exception handling
        _RAISE(*this);
    }
#endif // _HAS_EXCEPTIONS
};

_CRTIMP2_PURE const char* __CLRCALL_PURE_OR_CDECL _Syserror_map(int);
_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Winerror_map(int);
_CRTIMP2_PURE unsigned long __CLRCALL_PURE_OR_CDECL _Winerror_message(
    unsigned long _Message_id, char* _Narrow, unsigned long _Size);

// CLASS _Generic_error_category
class _Generic_error_category : public error_category { // categorize a generic error
public:
    _Generic_error_category() noexcept { // default constructor
        _Addr = _Generic_addr;
    }

    _NODISCARD virtual const char* name() const noexcept override { // get name of category
        return "generic";
    }

    _NODISCARD virtual string message(int _Errcode) const override { // convert to name of error
        return _Syserror_map(_Errcode);
    }
};

// CLASS _Iostream_error_category
class _Iostream_error_category : public _Generic_error_category { // categorize an iostream error
public:
    _Iostream_error_category() noexcept { // default constructor
        _Addr = _Iostream_addr;
    }

    _NODISCARD virtual const char* name() const noexcept override { // get name of category
        return "iostream";
    }

    _NODISCARD virtual string message(int _Errcode) const override { // convert to name of error
        if (_Errcode == (int) io_errc::stream) {
            return "iostream stream error";
        } else {
            return _Generic_error_category::message(_Errcode);
        }
    }
};

// CLASS _System_error_category
class _System_error_category : public error_category { // categorize an operating system error
public:
    _System_error_category() noexcept { // default constructor
        _Addr = _System_addr;
    }

    _NODISCARD virtual const char* name() const noexcept override { // get name of category
        return "system";
    }

    _NODISCARD virtual string message(int _Errcode) const override { // convert to name of error
        const unsigned long _Size = 32767;
        string _Narrow(_Size, '\0');

        const unsigned long _Val = _Winerror_message(static_cast<unsigned long>(_Errcode), &_Narrow[0], _Size);
        if (_Val == 0) {
            _Narrow = "unknown error";
        } else {
            _Narrow.resize(_Val);
        }

        _Narrow.shrink_to_fit();
        return _Narrow;
    }

    _NODISCARD virtual error_condition default_error_condition(int _Errval) const
        noexcept override { // make error_condition for error code (generic if possible)
        const int _Posv = _Winerror_map(_Errval);
        if (_Posv == 0) {
            return error_condition(_Errval, system_category());
        } else {
            return error_condition(_Posv, generic_category());
        }
    }
};

#ifdef _M_CEE_PURE
template <class _Ty>
struct _Immortalizer { // constructs _Ty, never destroys
    _Immortalizer() { // construct _Ty inside _Storage
        ::new (static_cast<void*>(&_Storage)) _Ty();
    }

    _Immortalizer(const _Immortalizer&) = delete;
    _Immortalizer& operator=(const _Immortalizer&) = delete;

    aligned_union_t<1, _Ty> _Storage;
};

template <class _Ty>
inline _Ty& _Immortalize() { // return a reference to an object that will live forever
    /* MAGIC */ static _Immortalizer<_Ty> _Static;
    return reinterpret_cast<_Ty&>(_Static._Storage);
}
#else // ^^^ _M_CEE_PURE ^^^ // vvv !_M_CEE_PURE vvv
template <class _Ty>
inline int __stdcall _Immortalize_impl(
    void*, void* _Storage_ptr, void**) noexcept { // adapt True Placement New to _Execute_once
    ::new (_Storage_ptr) _Ty();
    return 1;
}

template <class _Ty>
inline _Ty& _Immortalize() { // return a reference to an object that will live forever
    static_assert(sizeof(void*) == sizeof(once_flag), "TRANSITION, VSO#406237");
    static_assert(alignof(void*) == alignof(once_flag), "TRANSITION, VSO#406237");
    static void* _Flag = nullptr;
    static aligned_union_t<1, _Ty> _Storage;
    if (_Execute_once(reinterpret_cast<once_flag&>(_Flag), _Immortalize_impl<_Ty>, &_Storage)
        == 0) { // _Execute_once should never fail if the callback never fails
        _STD terminate();
    }

    return reinterpret_cast<_Ty&>(_Storage);
}
#endif // _M_CEE_PURE

_NODISCARD inline const error_category& generic_category() noexcept { // get generic_category
    return _Immortalize<_Generic_error_category>();
}

_NODISCARD inline const error_category& iostream_category() noexcept { // get iostream_category
    return _Immortalize<_Iostream_error_category>();
}

_NODISCARD inline const error_category& system_category() noexcept { // get system_category
    return _Immortalize<_System_error_category>();
}
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // RC_INVOKED
#endif // _SYSTEM_ERROR_

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V6.50:0009 */
